# -*- encoding: utf-8 -*-

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import gtk
import os
from  .. import client 
ec = client
from .. import main, img
from .. import  config as ecfg

COVER_SOURCE = 'client/generic/covers'
MAIN_PROP = 'picture_front'
EXTRA_PROP = 'picture_front_alternatives'

class MediaInfoDialog(gtk.Dialog):
    
    SOURCE = 'client/generic/override'

    def __init__(self, parent, control, minfo):
        gtk.Dialog.__init__(self, title = _("Media Info"), parent = parent,
                flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                buttons=(gtk.STOCK_SAVE, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE
                    )
                )
        self.control = control
        self.client = control.client
        self.changed = set()
        self.minfo = client.MediaInfo(minfo)
        notebook = gtk.Notebook()
        notebook.append_page(self.get_simple_tab(), gtk.Label(_("General")))
        notebook.append_page(self.get_advanced_tab(), gtk.Label(_("Extra Metadata")))
        self.vbox.add(notebook)
        self.client.connect('e_medialib_entry_changed', self.entry_changed)

    def entry_changed(self, client, id):
        def update_info(info):
            update_image = False
            new_img = ((info.has_key(MAIN_PROP)) and (info[MAIN_PROP])
                    or (None))
            old_img = ((self.minfo.has_key(MAIN_PROP)) and 
                    (self.minfo[MAIN_PROP]) or (None))
            for key in self.changed:
                info[key] = self.minfo[key]
            self.minfo = info
            self.update_simple()
            self.update_advanced()
            if old_img != new_img:
                self.set_albumart(hash = new_img)
        if id != self.minfo['id']:
            return False
        client.medialib.get_info( id, update_info, fast = True)


    def get_simple_tab(self):
        def entry_changed(widget, key):
            self.changed.add(key)
            self.minfo[key] = widget.get_text()
            self.update_advanced()
        def spin_changed(widget, key):
            self.changed.add(key)
            self.minfo[key] = str(widget.get_value_as_int())
            self.update_advanced()
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_NONE)
        table = gtk.Table(rows = 8, columns = 4)
        frame.add(table)
        labels = [
            (_('title'), 0, 1, 0, 1),
            (_('artist'), 0, 1, 1, 2),
            (_('album'), 0, 1, 2, 3),
            (_('url'), 0, 1, 3, 4),
            (_('genre'), 0, 1, 4, 5),
            (_('track'), 2, 3, 4, 5),
            (_('performer'), 0, 1, 5, 6),
            (_('year'), 2, 3, 5, 6),
            (_('copyright'), 0, 1, 6, 7),
            (_('publisher'), 2, 3, 6, 7),
            (_('comment'), 0, 1, 7, 8)]
        for label, col1, col2, row1, row2 in iter(labels):
            table.attach(gtk.Label(label.capitalize()+': '), col1, col2, row1, row2, xoptions = 0, yoptions = 0)

        self.entries = {}
        entries = [
            ('title', 1, 3, 0, 1),
            ('artist', 1, 3, 1, 2),
            ('album', 1, 3, 2, 3),
            ('genre', 1, 2, 4, 5),
            ('performer', 1, 2, 5, 6),
            ('copyright', 1, 2, 6, 7),
            ('publisher', 3, 4, 6, 7),
            ('comment', 1,4, 7, 8)]
        for entry_key, col1, col2, row1, row2 in iter(entries):
            entry = gtk.Entry()
            if self.minfo.has_key(entry_key):
                entry.set_text(self.minfo[entry_key])
            entry.connect('changed', entry_changed, entry_key)
            table.attach(entry, col1, col2, row1, row2, yoptions = 0)
            self.entries[entry_key] = entry
        entry = gtk.Entry()
        entry.set_editable(False)
        if self.minfo.has_key('url'):
            entry.set_text(client.decode_url(self.minfo['url']))
        table.attach(entry, 1, 3, 3, 4, yoptions = 0)
        self.entries['url'] = entry

        self.spins = {}
        tracknr = gtk.SpinButton(climb_rate = 1.0)
        tracknr.set_range(0, 999)
        tracknr.set_increments(1, 5)
        if self.minfo.has_key('tracknr'):
            try:
                tracknr.set_value(int(self.minfo['tracknr']))
            except:
                pass
        table.attach(tracknr, 3, 4, 4, 5, yoptions = 0)
        tracknr.connect('value-changed', spin_changed, 'tracknr')
        self.spins['tracknr'] = tracknr
        
        year = gtk.SpinButton(climb_rate = 1.0)
        year.set_range(0, 9999)
        year.set_increments(1, 5)
        if self.minfo.has_key('date'):
            try:
                year.set_value(int(self.minfo['date']))
            except:
                pass
        table.attach(year, 3, 4, 5, 6, yoptions = 0)
        year.connect('value-changed', spin_changed, 'date')
        self.spins['date'] = year

        self.cover_img = gtk.Image()
        cover_button = gtk.Button()
        cover_button.set_image(self.cover_img)
        md5_hash = None
        if self.minfo.has_key(MAIN_PROP):
            md5_hash = self.minfo[MAIN_PROP]
        self.set_albumart(hash = md5_hash)
        table.attach(cover_button, 3, 4, 0, 4, 
                xoptions = gtk.FILL, yoptions = gtk.FILL)
        cover_button.connect('clicked', self.show_albumart_chooser)

        frame.show_all()
        return frame
    
    def show_albumart_chooser(self, widget, data = None):
        """show the alternative cover chooser dialog and change the 
        medialib properties if necessary"""
        nfo = self.minfo
        dialog = AlternativeImageChooser(self, self.control, nfo)
        t = dialog.run()
        if t == gtk.RESPONSE_OK:
            dialog.apply_selected() 
        dialog.destroy()
    
    def get_advanced_tab(self):
        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_NONE)
        self.liststore = gtk.ListStore(str, str)
        self.liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
        treeview = gtk.TreeView(self.liststore)
        #prefname column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 0)

        column = gtk.TreeViewColumn(_("Name"), renderer, text = 0)
        treeview.append_column(column)

        #prefvalue column
        renderer = gtk.CellRendererText()
        renderer.set_data("column", 1)
        column = gtk.TreeViewColumn(_("Value"), renderer, text = 1)
        treeview.append_column(column)
        self.update_advanced()
        scrolledw = gtk.ScrolledWindow()
        scrolledw.add(treeview)
        scrolledw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
        frame.add(scrolledw)
        return frame
    
    def set_albumart(self, pixbuf = None, hash = None):
        """get the albumart image from the daemon"""
        def _bindata_cb(value):
            pb = img.get_pixbuf_from_data(value)
            self.set_albumart(pixbuf = pb)
        if hash != None:
            self.client.bindata.retrieve(hash, _bindata_cb)
            return
        elif pixbuf == None:
            pixbuf = gtk.icon_theme_get_default().load_icon('etude', 128, 0)
        pixbuf, w, h = img.get_pixbuf_of_size(pixbuf, 128)
        self.cover_img.set_from_pixbuf(pixbuf)

    def update_advanced(self):
        self.liststore.clear()
        info_dict = {}
        for key in self.minfo:
            if key[1] not in info_dict:
                info_dict[key[1]] = self.minfo[key[1]]
        for key, value in info_dict.iteritems():
            self.liststore.append( (_(key).capitalize(), ecfg.format_value(key, value)))

    def update_simple(self):
        for key, entry in self.entries.iteritems():
            if (key not in self.changed) and (self.minfo.has_key(key)):
                entry.set_text(self.minfo[key])

        for key, spin in self.spins.iteritems():
            if (key not in self.changed) and (self.minfo.has_key(key)):
                try:
                    spin.set_value(int(self.minfo[key]))
                except:
                    pass

    def save_info(self):
        """if the dialog.run() return OK, this should be
        run to save info to the daemon"""
        for key in self.changed:
            value = self.minfo[key]
            id = self.minfo['id']
            self.client.medialib.property_set(id, key, value, self.SOURCE)
        return False

def edit_songs(parent, control, ids):
    """for mass editing"""
    def show_dialog(info, index):
        next = False
        if info.has_key('id'):
            dialog = MediaInfoDialog(parent, control, 
                    info)
            if index != (len(ids)-1):
                dialog.add_button(gtk.STOCK_GO_FORWARD, gtk.RESPONSE_NO)
            dialog.show_all()
            res = dialog.run()
            if res == gtk.RESPONSE_OK:
                dialog.save_info()
                next = True
            if res == gtk.RESPONSE_NO:
                next = True
            dialog.destroy()
        else:
            next = True
        index += 1
        if next and (index < len(ids)):
            control.client.medialib.get_info(ids[index], cb=show_dialog, 
                    index = index, fast = True)
    if len(ids)>0:
        control.client.medialib.get_info( ids[0], cb=show_dialog, 
                index = 0, fast = True)


class AlternativeImageChooser(gtk.Dialog):

    def __init__(self, parent, control, info):
        gtk.Dialog.__init__(self, title = _("Alternative cover chooser"),
                parent = parent.get_toplevel(), 
                flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CLOSE,
                    gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        self.set_default_size(400, 400)
        self.client = control.client
        self.control = control
        
        self.store = gtk.ListStore(str, gtk.gdk.Pixbuf, str)
        self.iconview = gtk.IconView(self.store)
        self.iconview.set_text_column(0)
        self.iconview.set_pixbuf_column(1)

        self.client = parent.client
        self.info = ec.MediaInfo(info)
        if not self.info.has_key(MAIN_PROP):
            self.info[MAIN_PROP] = None
        if not self.info.has_key(EXTRA_PROP):
            self.info[EXTRA_PROP] = ''
        self.__fill_store()
        scrolled = gtk.ScrolledWindow()
        scrolled.set_policy( gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        scrolled.add(self.iconview)
        self.vbox.pack_start(scrolled, True, True)
        
        if self.control.has_interface('query_album_covers'):
            table = gtk.Table(rows = 3, columns = 3)
            self.message = gtk.Label()
            table.attach(self.message, 0,3, 2,3, xoptions=0, yoptions= 0)
            self.artist_entry = gtk.Entry()
            if self.info.has_key('artist'):
                self.artist_entry.set_text(self.info['artist'])
            self.album_entry = gtk.Entry()
            if self.info.has_key('album'):
                self.album_entry.set_text(self.info['album'])
        
            table.attach(gtk.Label(_("artist").capitalize()+":"), 0, 1, 0, 1, 
                    xoptions = 0, yoptions = 0)
            table.attach(self.artist_entry, 1, 2, 0, 1, 
                    xoptions = gtk.FILL|gtk.EXPAND, yoptions = 0)
        
            table.attach(gtk.Label(_("album").capitalize()+":"), 0, 1, 1, 2, 
                    xoptions = 0, yoptions = 0)
            table.attach(self.album_entry, 1, 2, 1, 2, 
                    xoptions = gtk.FILL|gtk.EXPAND, yoptions = 0)
            find_button = gtk.Button(stock = gtk.STOCK_FIND)
            find_button.connect('clicked', self.update_alternatives)
            table.attach(find_button, 2, 3, 0, 2, xoptions = 0, yoptions = 0)
            expand = gtk.Expander(label=_("Find more"))
            expand.add(table)
            self.vbox.pack_start(expand, False, False)

            add_local = gtk.Button(_("Add local image"))
            align = gtk.Alignment(1.0, 1.0)
            align.add(add_local)
            self.vbox.pack_start(align, False, False)
            add_local.connect('clicked', self.local_chooser)
        
        self.show_all()
        self.client.connect('e_medialib_entry_changed', self.covers_changed)
        self.iconview.connect('button-press-event', self.on_show_popup)
    
    def image_filter_func(self, filter_info, data = None):
        #filter info tuple - (full_path, uri, display name, mime)
        return filter_info[3].startswith("image/")


    def on_show_popup(self, widget, event):
        if event.button == 3:
            #x, y = widget.convert_widget_to_bin_window_coords(
            #        int(event.x), int(event.y))
            path = widget.get_path_at_pos(event.x, event.y)
            if path == None:
                return False
            menu = gtk.Menu()
            item = gtk.ImageMenuItem( gtk.STOCK_REMOVE)
            item.connect('activate', self.remove_selected, 
                    self.store[path][2])
            menu.append(item)    
            menu.show_all()
            menu.popup(None, None, None, event.button, event.time)

    def local_chooser(self, widget, data= None):
        nfo = self.info
        dialog = gtk.FileChooserDialog( title=None, action=gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
                    gtk.STOCK_APPLY, gtk.RESPONSE_OK
                    ))
        dialog.set_select_multiple(False)
        filter = gtk.FileFilter()
        filter.set_name(_("Images"))
        filter.add_custom( gtk.FILE_FILTER_MIME_TYPE, self.image_filter_func, None)
        dialog.add_filter(filter)
        
        filter = gtk.FileFilter()
        filter.set_name(_("All Files"))
        filter.add_pattern( "*")
        dialog.add_filter(filter)
        if not self.control.profile.is_remote():
            if nfo.has_key('url'):
                begin, sep, end = nfo['url'].rpartition('/')
                uri = ec.decode_url(begin)
                dialog.set_current_folder_uri(uri)
            else:
                dialog.set_current_folder(self.control.profile['music_dir'])
        
        t = dialog.run()
        if t == gtk.RESPONSE_OK:
            artist, album = (None, None)
            if nfo.has_key('artist'):
                artist =nfo['artist']
            if nfo.has_key('album'):
                album =nfo['album']
            if artist and (artist != '') and album and (album != ''):
                id = None
            else:
                id = nfo['id']
        
            print(id, artist, album)
            path = dialog.get_filename()
            if path != None:
                self.control.use_interface('query_album_covers', 
                        (id, artist, album, 'file://'+path))
        dialog.destroy()
        return False

    def remove_selected(self, widget, hash):
        self.remove_hash(hash)

    def covers_changed(self, client, id):
        def get_changed_info(new_info):
            if new_info == None:
                self.destroy()
                return
            if not new_info.has_key(MAIN_PROP):
                new_info[MAIN_PROP] = None
            if not new_info.has_key(EXTRA_PROP):
                new_info[EXTRA_PROP] = ''
            if new_info[EXTRA_PROP] != self.info[EXTRA_PROP]:
                self.info = new_info
                self.__fill_store()
            else:
                self.info = new_info
        if id == self.info['id']:
            self.client.medialib.get_info(id, get_changed_info)

    def update_alternatives(self, widget):
        artist = self.artist_entry.get_text()
        album = self.album_entry.get_text()
        if len(artist) == 0 or len(album) == 0:
            self.message.set_text(_("Please set both artist and album"))
            return
        self.message.set_text('')
        self.control.use_interface('query_album_covers', (self.info['id'], artist, album))

    def __fill_store(self):
        self.store.clear()
        for hash in self.info[EXTRA_PROP].split(';'):
            if len(hash) == 0:
                continue
            self.client.bindata.retrieve(hash, self.__fill_store_cb, cb_args = {'hash': hash})

    def __fill_store_cb(self, data, hash = ''):
        if data == None:
            if hash:
                self.remove_hash(hash)
            return
        pixbuf = img.get_pixbuf_from_data(data)
        size = str(pixbuf.get_width())+" x "+str(pixbuf.get_height())
        self.store.append( (size, pixbuf, hash))

    def get_selected_pic(self):
        paths = self.iconview.get_selected_items()
        if len(paths) == 0:
            return None
        for path in paths:
            iter = self.store.get_iter(path)
            hash = self.store.get_value(iter, 2)
            return hash

    def remove_hash(self, hash):
        def _set_hashes(row_list, hash = None):
            for dict in row_list:
                id, main, extra = (dict['id'], dict[MAIN_PROP],
                        dict[EXTRA_PROP])
                if main and (hash == main):
                    self.client.medialib.property_set(id, MAIN_PROP, '', COVER_SOURCE)
                if extra: 
                    extra_sp = extra.split(';')
                    if (hash not in extra_sp):
                        continue
                    extra_sp = [ext for ext in extra_sp 
                            if (ext!='') and (ext!=hash)]
                    self.client.medialib.property_set(id, EXTRA_PROP, 
                            ';'.join(extra_sp), COVER_SOURCE)

        if not hash:
            return
        self.client.bindata.remove(hash)
        cl = self.client.collection
        coll = cl.Union(cl.Match(field = MAIN_PROP, value = '*'+hash+'*'), 
            cl.Match(field = EXTRA_PROP, value = '*'+hash+'*'))
        cl.query_infos(coll = coll, fields=['id', MAIN_PROP, EXTRA_PROP],
                cb = _set_hashes, cb_args = {'hash': hash})

    def apply_selected(self):
        """apply the selection"""
        def _set_hashes( id_list, hash = None, alternatives = None):
            for id in id_list:
                self.client.medialib.property_set(id, MAIN_PROP, hash, COVER_SOURCE)
                self.client.medialib.property_set(id, EXTRA_PROP, alternatives, COVER_SOURCE)
            return False

        hash = self.get_selected_pic()
        if hash == self.info[MAIN_PROP] or hash == None:
            return False
        try:
            alternatives = self.info[EXTRA_PROP]
            ind = alternatives.index(hash)
            alternatives = alternatives.replace(hash, '')
            try:
                if alternatives[ind] == ';':
                    alternatives = alternatives[:ind-1]+alternatives[ind:]
            except IndexError:
                pass
            if len(alternatives) > 0:
                alternatives += ';'
            alternatives += self.info[MAIN_PROP]
        except:
            print(sys.exc_info())
        cl = self.client.collection
        try:
            coll = cl.Intersection(cl.Equals(field = 'artist', value = self.info['artist'], 
                **{'case-sensitive': 'true'}), 
                cl.Equals(field = 'album', value = self.info['album'], 
                    **{'case-sensitive':'true'})
                )
            cl.query_ids(coll = coll, cb = _set_hashes, cb_args = {'hash': hash,
                'alternatives': alternatives})
        except KeyError:
            _set_hashes([self.info['id'],], hash = hash, alternatives = alternatives)

